package com.interview.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 交替输出奇偶数的线程池示例
 * 
 * 功能说明：
 * - 使用 ExecutorService 线程池创建两个线程
 * - 线程1 输出所有奇数（1, 3, 5, ..., 99）
 * - 线程2 输出所有偶数（2, 4, 6, ..., 100）
 * - 通过 Lock + Condition 机制确保输出顺序严格按照数字大小递增
 * - 输出结果：1, 2, 3, 4, 5, ..., 98, 99, 100
 * 
 * 同步机制说明：
 * - 使用 ReentrantLock 作为互斥锁，保证同一时刻只有一个线程执行
 * - 使用 Condition 实现线程间的等待/唤醒机制
 * - 通过 currentNumber 共享变量追踪当前应该输出的数字
 * - 每个线程输出数字后，检查下一个数字是否应该由自己输出
 *   - 如果是，继续输出；如果不是，等待另一个线程唤醒
 * 
 * @author Interview
 * @version 1.0
 */
public class AlternateNumberPrinter {
    
    /**
     * 共享资源类：管理奇偶数的输出状态
     */
    static class NumberPrinter {
        // 当前应该输出的数字
        private int currentNumber = 1;
        
        // 互斥锁
        private final Lock lock = new ReentrantLock();
        
        // 条件变量，用于线程间的等待/唤醒
        private final Condition condition = lock.newCondition();
        
        // 最大数字
        private static final int MAX_NUMBER = 100;
        
        /**
         * 输出奇数的方法
         * 
         * 逻辑说明：
         * 1. 获取锁，进入临界区
         * 2. 循环检查当前数字是否为奇数
         * 3. 如果是奇数且未超过100，输出该数字，然后递增
         * 4. 如果不是奇数，等待另一个线程唤醒
         * 5. 释放锁
         */
        public void printOdd() {
            lock.lock();
            try {
                while (currentNumber <= MAX_NUMBER) {
                    // 如果当前数字是奇数，输出它
                    if (currentNumber % 2 == 1) {
                        System.out.print(currentNumber + " ");
                        currentNumber++;
                        // 唤醒等待的线程（偶数线程）
                        condition.signalAll();
                    } else {
                        // 当前数字是偶数，等待偶数线程处理
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            } finally {
                lock.unlock();
            }
        }
        
        /**
         * 输出偶数的方法
         * 
         * 逻辑说明：
         * 1. 获取锁，进入临界区
         * 2. 循环检查当前数字是否为偶数
         * 3. 如果是偶数且未超过100，输出该数字，然后递增
         * 4. 如果不是偶数，等待另一个线程唤醒
         * 5. 释放锁
         */
        public void printEven() {
            lock.lock();
            try {
                while (currentNumber <= MAX_NUMBER) {
                    // 如果当前数字是偶数，输出它
                    if (currentNumber % 2 == 0) {
                        System.out.print(currentNumber + " ");
                        currentNumber++;
                        // 唤醒等待的线程（奇数线程）
                        condition.signalAll();
                    } else {
                        // 当前数字是奇数，等待奇数线程处理
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }
    
    /**
     * 主方法：演示线程池中两个线程协作输出奇偶数
     * 
     * 执行流程：
     * 1. 创建一个包含2个线程的固定大小线程池
     * 2. 创建共享的 NumberPrinter 对象
     * 3. 提交两个任务到线程池：
     *    - 任务1：输出奇数
     *    - 任务2：输出偶数
     * 4. 等待所有任务完成
     * 5. 关闭线程池
     */
    public static void main(String[] args) {
        System.out.println("========== 线程池协作输出奇偶数示例 ==========\n");
        System.out.println("预期输出：1 2 3 4 5 ... 98 99 100\n");
        System.out.println("实际输出：");
        
        // 创建一个包含2个线程的固定大小线程池
        // newFixedThreadPool(2) 会创建一个包含2个线程的线程池
        // 这两个线程会被重复使用来执行提交的任务
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        
        try {
            // 创建共享的 NumberPrinter 对象
            // 这个对象会被两个线程共享，用于协调它们的输出
            NumberPrinter printer = new NumberPrinter();
            
            // 提交奇数输出任务到线程池
            // 这个任务会在线程池中的一个线程上执行
            executorService.submit(() -> {
                printer.printOdd();
            });
            
            // 提交偶数输出任务到线程池
            // 这个任务会在线程池中的另一个线程上执行
            executorService.submit(() -> {
                printer.printEven();
            });
            
            // 关闭线程池，不再接受新任务
            executorService.shutdown();
            
            // 等待所有任务完成
            // 这会阻塞当前线程，直到线程池中的所有任务都完成
            if (!executorService.awaitTermination(10, java.util.concurrent.TimeUnit.SECONDS)) {
                System.err.println("\n线程池未在规定时间内关闭");
                executorService.shutdownNow();
            }
            
            System.out.println("\n\n========== 程序执行完毕 ==========");
            
        } catch (InterruptedException e) {
            System.err.println("主线程被中断");
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

